#include <string.h>
#include "selector.h"
#include "context.h"
#include "flv.h"
#include "str.h"
#include "opt.h"

struct object {
	long time[2];
	int time_set[2];
	unsigned long ord[2];
	int ord_set[2];
};

static struct {
	struct object tag, gop;
	unsigned track;
	int track_set;
} sel;

static int complement;

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

static int scan_track(const struct option_def *def, const char *arg)
{
	unsigned i;
	unsigned long ul;
	(void)def;
	for (i = 0; i < ARRAY_SIZE(flv_track_str); i++)
		if (flv_track_str[i] &&
		!strncmp(flv_track_str[i], arg, strlen(arg))) {
			sel.track = i;
			sel.track_set = 1;
			return 0;
		}
	if (strtoulong(arg, &ul) < 0)
		return -1;
	sel.track = ul;
	sel.track_set = 1;
	return 0;
}

static int scan_time(const struct option_def *def, const char *arg)
{
	char buf[strlen(arg) + 1];
	char *a, *b;
	struct object *obj;

	if (def->shortname == 'T')
		obj = &sel.gop;
	else
		obj = &sel.tag;

	strcpy(buf, arg);
	split(buf, &a, &b);
	if (*a) {
		if (strtotime(a, &obj->time[0]) < 0)
			return -1;
		obj->time_set[0] = 1;
	}
	if (*b) {
		if (strtotime(b, &obj->time[1]) < 0)
			return -1;
		obj->time_set[1] = 1;
	}
	return 0;
}

static int scan_ord(const struct option_def *def, const char *arg)
{
	char buf[strlen(arg) + 1];
	char *a, *b;
	struct object *obj;

	if (def->shortname == 'N')
		obj = &sel.gop;
	else
		obj = &sel.tag;

	strcpy(buf, arg);
	split(buf, &a, &b);
	if (*a) {
		if (strtoulong(a, &obj->ord[0]) < 0)
			return -1;
		obj->ord_set[0] = 1;
	}
	if (*b) {
		if (strtoulong(b, &obj->ord[1]) < 0)
			return -1;
		obj->ord_set[1] = 1;
	}
	return 0;
}

static const struct option_def defs[] = {
	{OPT_FLAG, 'C', "complement", {.flag = &complement}, "complement the selection", 0},
	{OPT_FUNC, 'n', "number",     {.func = scan_ord}, "tag ordinal selector", "NUMBER-RANGE"},
	{OPT_FUNC, 'N', "number-gop", {.func = scan_ord}, "gop ordinal selector", "NUMBER-RANGE"},
	{OPT_FUNC, 'k', "track",      {.func = scan_track}, "tag track selector", "TRACK"},
	{OPT_FUNC, 't', "time",       {.func = scan_time}, "tag time selector", "TIME-RANGE"},
	{OPT_FUNC, 'T', "time-gop",   {.func = scan_time}, "gop time selector", "TIME-RANGE"},
	{.type = OPT_END},
};

const struct option_group selector_options = {
	.name = "Selectors",
	.defs = defs,
};

static int obj_selected(const struct object *obj, unsigned ord, int time)
{
	if (obj->ord_set[0] && ord < obj->ord[0])
		return 0;
	if (obj->ord_set[1] && ord > obj->ord[1])
		return 0;
	if (obj->time_set[0] && time < obj->time[0])
		return 0;
	if (obj->time_set[1] && time > obj->time[1])
		return 0;
	if (obj->time_set[0] && obj->time_set[1] &&
	obj->time[0] != obj->time[1] && time == obj->time[1])
		return 0;
	return 1;
}

/**
 * is_selected() - check if current object is selected by selector
 *
 * Positions are always inclusive. [a, a]
 * Ordinal ranges are inclusive on both sides. [a, b]
 * Time ranges are left-inclusive and right-exclusive. [a, b)
 */
int is_selected(const struct context *ctx)
{
	if (!obj_selected(&sel.tag, ctx->tag[0].ord, ctx->tag[0].time))
		return complement;
	if (!obj_selected(&sel.gop, ctx->gop[0].ord, ctx->gop[0].first.time))
		return complement;
	if (sel.track_set && ctx->tag[0].track != sel.track)
		return complement;
	return !complement;
}

#ifndef NDEBUG
#include <stdio.h>
static int print_object(const struct object *obj)
{
	int r = 0;
	if (obj->ord_set[0] || obj->ord_set[1]) {
		printf("ord:");
		if (obj->ord_set[0] && obj->ord_set[1] &&
		obj->ord[0] == obj->ord[1]) {
			printf("%lu", obj->ord[0]);
		} else {
			if (obj->ord_set[0])
				printf("%lu", obj->ord[0]);
			putchar(',');
			if (obj->ord_set[1])
				printf("%lu", obj->ord[1]);
		}
		r = 1;
	}
	if (obj->time_set[0] || obj->time_set[1]) {
		printf("%stime:", r ? "/" : "");
		if (obj->time_set[0] && obj->time_set[1] &&
		obj->time[0] == obj->time[1]) {
			printf("%ld", obj->time[0]);
		} else {
			if (obj->time_set[0])
				printf("%ld", obj->time[0]);
			putchar(',');
			if (obj->time_set[1])
				printf("%ld", obj->time[1]);
		}
		r = 1;
	}
	return r;
}
void print_selector(void);
void print_selector(void)
{
	int r;
	printf("tag-selector=");
	r = print_object(&sel.tag);
	if (sel.track_set)
		printf("%strack:%u", r ? "/" : "", sel.track);
	printf(" gop-selector=");
	print_object(&sel.gop);
	putchar('\n');
}
#endif
